
#include "GlobalHead.h"

#ifndef _PreComp_
# include <sstream>
# include <climits>
#endif

#include <boost/graph/topological_sort.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/bind.hpp>


#include "Document.h"
//#include "DocumentPy.h"
#include "App_Application.h"
#include "DocumentObject.h"
#include "PropertyLinks.h"


 #include <Base/Console.h>
// #include <Base/Exception.h>
 #include <Base/FileInfo.h>
 #include <Base/TimeInfo.h>
 #include <Base/Interpreter.h>
 #include <Base/Reader.h>
 #include <Base/Writer.h>
 #include <Base/Stream.h>
 #include <Base/FileInfo.h>
#include <Base/Tools.h>
 #include <Base/Uuid.h>

 #include <zipios++/zipios-config.h>
 #include <zipios++/zipfile.h>
 #include <zipios++/zipinputstream.h>
 #include <zipios++/zipoutputstream.h>
 #include <zipios++/meta-iostreams.h>

#include "App_Application.h"
#include "CustomPlayer.h"
#include "Transactions.h"

#include "App/VRMLObject.h"

// using Base::Console;
 //using Base::streq;
 using Base::Writer;
using namespace App;
using namespace std;
using namespace boost;
using namespace zipios;

#ifdef MemDebugOn
# define new DEBUG_CLIENTBLOCK
#endif

#if FC_DEBUG
#  define FC_LOGFEATUREUPDATE
#endif 

// typedef boost::property<boost::vertex_root_t, DocumentObject* > VertexProperty;
typedef boost::adjacency_list <
boost::vecS,           // class OutEdgeListS  : a Sequence or an AssociativeContainer
boost::vecS,           // class VertexListS   : a Sequence or a RandomAccessContainer
boost::directedS,      // class DirectedS     : This is a directed graph
boost::no_property,    // class VertexProperty:
boost::no_property,    // class EdgeProperty:
boost::no_property,    // class GraphProperty:
boost::listS           // class EdgeListS:
> DependencyList;
typedef boost::graph_traits<DependencyList> Traits;
typedef Traits::vertex_descriptor Vertex;
typedef Traits::edge_descriptor Edge;

namespace App {

// Pimpl class
struct DocumentP
{
    // Array to preserve the creation order of created objects
    std::vector<DocumentObject*> objectArray;
    std::map<std::string,DocumentObject*> objectMap;
    DocumentObject* activeObject;
    Transaction *activeUndoTransaction;
    Transaction *activeTransaction;
    int iTransactionMode;
    int iTransactionCount;
    std::map<int,Transaction*> mTransactions;
    std::map<Vertex,DocumentObject*> vertexMap;
    bool rollback;
    bool closable;
    int iUndoMode;

    DocumentP() {
        activeObject = 0;
        activeUndoTransaction = 0;
        activeTransaction = 0;
        iTransactionMode = 0;
        iTransactionCount = 0;
        rollback = false;
        closable = true;
        iUndoMode = 0;
    }
};

} // namespace App

PROPERTY_SOURCE(App::Document, App::PropertyContainer)



void Document::writeDependencyGraphViz(std::ostream &out)
{
    //  // caching vertex to DocObject
    //std::map<Vertex,DocumentObject*> VertexMap;
    //for(std::map<DocumentObject*,Vertex>::const_iterator It1= _DepConMap.begin();It1 != _DepConMap.end(); ++It1)
    //  VertexMap[It1->second] = It1->first;

    out << "digraph G {" << endl;
    out << "\tordering=out;" << endl;
    out << "\tnode [shape = box];" << endl;

    for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
        out << "\t" << It->first << ";" <<endl;
        std::vector<DocumentObject*> OutList = It->second->getOutList();
        for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
            if (*It2)
                out << "\t" << It->first << "->" << (*It2)->getNameInDocument() << ";" <<endl;
    }

    /*
    graph_traits<DependencyList>::edge_iterator ei, ei_end;
    for (tie(ei,ei_end) = edges(_DepList); ei != ei_end; ++ei)
      out << "\t"
          << VertexMap[source(*ei, _DepList)]->getNameInDocument()
          << " -> "
          << VertexMap[target(*ei, _DepList)]->getNameInDocument()
          << ";" << endl;
    */
    out << "}" << endl;
}

//bool _has_cycle_dfs(const DependencyList & g, vertex_t u, default_color_type * color)
//{
//  color[u] = gray_color;
//  graph_traits < DependencyList >::adjacency_iterator vi, vi_end;
//  for (tie(vi, vi_end) = adjacent_vertices(u, g); vi != vi_end; ++vi)
//    if (color[*vi] == white_color)
//      if (has_cycle_dfs(g, *vi, color))
//        return true;            // cycle detected, return immediately
//      else if (color[*vi] == gray_color)        // *vi is an ancestor!
//        return true;
//  color[u] = black_color;
//  return false;
//}

bool Document::checkOnCycle(void)
{/*
  std::vector < default_color_type > color(num_vertices(_DepList), white_color);
  graph_traits < DependencyList >::vertex_iterator vi, vi_end;
  for (tie(vi, vi_end) = vertices(_DepList); vi != vi_end; ++vi)
    if (color[*vi] == white_color)
      if (_has_cycle_dfs(_DepList, *vi, &color[0]))
        return true; */
    return false;
}

bool Document::undo(void)
{
//     if (d->iUndoMode) {
//         if (d->activeUndoTransaction)
//             commitTransaction();
//         else
//             assert(mUndoTransactions.size()!=0);
// 
//         // redo
//         d->activeUndoTransaction = new Transaction();
//         d->activeUndoTransaction->Name = mUndoTransactions.back()->Name;
// 
//         // applying the undo
//         mUndoTransactions.back()->apply(*this/*,DocChange*/);
// 
//         // save the redo
//         mRedoTransactions.push_back(d->activeUndoTransaction);
//         d->activeUndoTransaction = 0;
// 
//         delete mUndoTransactions.back();
//         mUndoTransactions.pop_back();
//     }

    return false;
}

bool Document::redo(void)
{
    //if (d->iUndoMode) {
    //    if (d->activeUndoTransaction)
    //        commitTransaction();

    //    assert(mRedoTransactions.size()!=0);

    //    // undo
    //    d->activeUndoTransaction = new Transaction();
    //    d->activeUndoTransaction->Name = mRedoTransactions.back()->Name;

    //    // do the redo
    //    mRedoTransactions.back()->apply(*this/*,DocChange*/);
    //    mUndoTransactions.push_back(d->activeUndoTransaction);
    //    d->activeUndoTransaction = 0;

    //    delete mRedoTransactions.back();
    //    mRedoTransactions.pop_back();
    //}

    return false;
}

std::vector<std::string> Document::getAvailableUndoNames() const
{
    std::vector<std::string> vList;
//     if (d->activeUndoTransaction)
//         vList.push_back(d->activeUndoTransaction->Name);
//     for (std::list<Transaction*>::const_reverse_iterator It=mUndoTransactions.rbegin();It!=mUndoTransactions.rend();++It)
//         vList.push_back((**It).Name);
    return vList;
}

std::vector<std::string> Document::getAvailableRedoNames() const
{
    std::vector<std::string> vList;
//     for (std::list<Transaction*>::const_reverse_iterator It=mRedoTransactions.rbegin();It!=mRedoTransactions.rend();++It)
//         vList.push_back((**It).Name);
    return vList;
}

void Document::openTransaction(const char* name)
{
//     if (d->iUndoMode) {
//         if (d->activeUndoTransaction)
//             commitTransaction();
//         _clearRedos();
// 
//         d->activeUndoTransaction = new Transaction();
//         if (name)
//             d->activeUndoTransaction->Name = name;
//     }
}

void Document::_clearRedos()
{
    while (!mRedoTransactions.empty()) {
        delete mRedoTransactions.back();
        mRedoTransactions.pop_back();
    }
}

void Document::commitTransaction()
{
    if (d->activeUndoTransaction) {
        mUndoTransactions.push_back(d->activeUndoTransaction);
        d->activeUndoTransaction = 0;
    }
}

void Document::abortTransaction()
{
//     if (d->activeUndoTransaction) {
//         d->rollback = true;
//         // applieing the so far made changes
//         d->activeUndoTransaction->apply(*this);
//         d->rollback = false;
// 
//         // destroy the undo
//         delete d->activeUndoTransaction;
//         d->activeUndoTransaction = 0;
//     }
}

void Document::clearUndos()
{
    if (d->activeUndoTransaction)
        commitTransaction();

    // When cleaning up the undo stack we must delete the transactions from front
    // to back because a document object can appear in several transactions but
    // once removed from the document the object can never ever appear in any later
    // transaction. Since the document object may be also deleted when the transaction
    // is deleted we must make sure not access an object once it's destroyed. Thus, we
    // go from front to back and not the other way round.
    while (!mUndoTransactions.empty()) {
        delete mUndoTransactions.front();
        mUndoTransactions.pop_front();
    }
    //while (!mUndoTransactions.empty()) {
    //    delete mUndoTransactions.back();
    //    mUndoTransactions.pop_back();
    //}

    _clearRedos();
}

int Document::getAvailableUndos() const
{
    if (d->activeUndoTransaction)
        return static_cast<int>(mUndoTransactions.size() + 1);
    else
        return static_cast<int>(mUndoTransactions.size());
}

int Document::getAvailableRedos() const
{
    return static_cast<int>(mRedoTransactions.size());
}

void Document::setUndoMode(int iMode)
{
    if (d->iUndoMode && !iMode)
        clearUndos();

    d->iUndoMode = iMode;
}

int Document::getUndoMode(void) const
{
    return d->iUndoMode;
}

unsigned int Document::getUndoMemSize (void) const
{
    return 0;
}

void Document::onChanged(const Property* prop)
{
//     // the Name property is a label for display purposes
     if (prop == &Label)
         GetApp_Application().signalRelabelDocument(*this);
}

void Document::onBeforeChangeProperty(const DocumentObject *Who, const Property *What)
{
     if (d->activeUndoTransaction && !d->rollback)
         d->activeUndoTransaction->addObjectChange(Who,What);
}

void Document::onChangedProperty(const DocumentObject *Who, const Property *What)
{
     if (d->activeTransaction && !d->rollback)
         d->activeTransaction->addObjectChange(Who,What);
     signalChangedObject(*Who, *What);
}

void Document::setTransactionMode(int iMode)
{
    /*  if(_iTransactionMode == 0 && iMode == 1)
        beginTransaction();

      if(activTransaction && iMode == 0)
        endTransaction();
      */
    d->iTransactionMode = iMode;
}

#if 0
/// starts a new transaction
int  Document::beginTransaction(void)
{
    if (activTransaction)
        endTransaction();

    iTransactionCount++;

    activTransaction = new Transaction(iTransactionCount);
    d->mTransactions[iTransactionCount] = activTransaction;

    return iTransactionCount;
}

/// revert all changes to the document since beginTransaction()
void Document::rollbackTransaction(void)
{
    // ToDo
    assert(0);
    endTransaction();
}

/// ends the open Transaction
int  Document::endTransaction(void)
{
    activTransaction = 0;
    return iTransactionCount;
}

/// returns the named Transaction
const Transaction *Document::getTransaction(int pos) const
{
    if (pos == -1)
        return activTransaction;
    else {
        std::map<int,Transaction*>::const_iterator Pos(d->mTransactions.find(pos));
        if (Pos != d->mTransactions.end())
            return Pos->second;
        else
            return 0;
    }
}
#endif

//--------------------------------------------------------------------------
// constructor
//--------------------------------------------------------------------------
Document::Document(void)
{
    // Remark: In a constructor we should never increment a Python object as we cannot be sure
    // if the Python interpreter gets a reference of it. E.g. if we increment but Python don't
    // get a reference then the object wouldn't get deleted in the destructor.
    // So, we must increment only if the interpreter gets a reference.
    // Remark: We force the document Python object to own the DocumentPy instance, thus we don't
    // have to care about ref counting any more.
//     DocumentPythonObject = Py::Object(new DocumentPy(this), true);
     d = new DocumentP;
// 
// #ifdef FC_LOGUPDATECHAIN
//     Console().Log("+App::Document: %p\n",this);
// #endif
// 
     ADD_PROPERTY_TYPE(Label,("Unnamed"),0,Prop_None,"The name of the document");
     ADD_PROPERTY_TYPE(FileName,(""),0,Prop_None,"The path to the file where the document is saved to");
     ADD_PROPERTY_TYPE(CreatedBy,(""),0,Prop_None,"The creator of the document");
     ADD_PROPERTY_TYPE(CreationDate,(Base::TimeInfo::currentDateTimeString()),0,Prop_ReadOnly,"Date of creation");
     ADD_PROPERTY_TYPE(LastModifiedBy,(""),0,Prop_None,0);
     ADD_PROPERTY_TYPE(LastModifiedDate,("Unknown"),0,Prop_ReadOnly,"Date of last modification");
     ADD_PROPERTY_TYPE(Company,(""),0,Prop_None,"Additional tag to save the the name of the company");
     ADD_PROPERTY_TYPE(Comment,(""),0,Prop_None,"Additional tag to save a comment");
     // create the uuid for the document
     Base::Uuid id;
     ADD_PROPERTY_TYPE(Id,(id.UuidStr),0,Prop_None,"UUID of the document");
 
     // create transient directory
     std::string basePath = Base::FileInfo::getTempPath() + GetApp_Application().getExecutableName();
     Base::FileInfo TransDir(basePath + "_Doc_" + id.UuidStr);
     if (!TransDir.exists())
         TransDir.createDirectory();
     ADD_PROPERTY_TYPE(TransientDir,(TransDir.filePath().c_str()),0,Prop_Transient,
         "Transient directory, where the files live while the document is open");
}

Document::~Document()
{
// #ifdef FC_LOGUPDATECHAIN
//     Console().Log("-App::Document: %s %p\n",getName(), this);
// #endif
// 
     clearUndos();
// 
     std::map<std::string,DocumentObject*>::iterator it;
// 
// #ifdef FC_LOGUPDATECHAIN
//     Console().Log("-Delete Features of %s \n",getName());
// #endif
// 
     d->objectArray.clear();
     for (it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
         delete(it->second);
     }
// 
//     // Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed
//     // Python object or not. In the constructor we forced the wrapper to own the object so we need
//     // not to dec'ref the Python object any more.
//     // But we must still invalidate the Python object because it need not to be
//     // destructed right now because the interpreter can own several references to it.
//     Base::PyObjectBase* doc = (Base::PyObjectBase*)DocumentPythonObject.ptr();
//     // Call before decrementing the reference counter, otherwise a heap error can occur
//     doc->setInvalid();
// 
//     // remove Transient directory
//     Base::FileInfo TransDir(TransientDir.getValue());
//     TransDir.deleteDirectoryRecursive();
    delete d;
}

//--------------------------------------------------------------------------
// Exported functions
//--------------------------------------------------------------------------

void Document::Save (Base::Writer &writer) const
{
    writer.Stream() << "<?xml version='1.0' encoding='utf-8'?>" << endl
    << "<!--" << endl
    << " FreeCAD Document, see http://free-cad.sourceforge.net for more information..." << endl
    << "-->" << endl;

    writer.Stream() << "<Document SchemaVersion=\"4\">" << endl;

    PropertyContainer::Save(writer);

    // writing the features types
    writer.incInd(); // indention for 'Objects count'
    writer.Stream() << writer.ind() << "<Objects Count=\"" << d->objectArray.size() <<"\">" << endl;

    writer.incInd(); // indention for 'Object type'
    std::vector<DocumentObject*>::const_iterator it;
	for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
		writer.Stream() << writer.ind() << "<Object "
			<< "type=\"" << (*it)->getTypeId().getName() << "\" "
			<< "name=\"" << (*it)->getNameInDocument()       << "\" "
			<< "/>" << endl;
	}

    writer.decInd();  // indention for 'Object type'
    writer.Stream() << writer.ind() << "</Objects>" << endl;

    // writing the features itself
    writer.Stream() << writer.ind() << "<ObjectData Count=\"" << d->objectArray.size() <<"\">" << endl;

    writer.incInd(); // indention for 'Object name'
    for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
        writer.Stream() << writer.ind() << "<Object name=\"" << (*it)->getNameInDocument() << "\">" << endl;
        (*it)->Save(writer);
        writer.Stream() << writer.ind() << "</Object>" << endl;
    }

    writer.decInd(); // indention for 'Object name'
    writer.Stream() << writer.ind() << "</ObjectData>" << endl;
    writer.decInd();  // indention for 'Objects count'
    writer.Stream() << "</Document>" << endl;
}

void Document::Restore(Base::XMLReader &reader)
{
    int i,Cnt;
    reader.readElement("Document");
    long scheme = reader.getAttributeAsInteger("SchemaVersion");
    reader.DocumentSchema = scheme;

    // When this document was created the FileName and Label properties
    // were set to the absolute path or file name, respectively. To save
    // the document to the file it was loaded from or to show the file name
    // in the tree view we must restore them after loading the file because
    // they will be overridden.
    // Note: This does not affect the internal name of the document in any way
    // that is kept in Application.
    std::string FilePath = FileName.getValue();
    std::string DocLabel = Label.getValue();

    // remove previous Transient directory
    Base::FileInfo TransDir(TransientDir.getValue());
    TransDir.deleteDirectoryRecursive();


    // read the Document Properties
    PropertyContainer::Restore(reader);

    // We must restore the correct 'FileName' property again because the stored
    // value could be invalid.
    FileName.setValue(FilePath.c_str());
    Label.setValue(DocLabel.c_str());

    // create new transient directory
    std::string basePath = Base::FileInfo::getTempPath() + GetApp_Application().getExecutableName();
    Base::FileInfo TransDirNew(basePath + "_Doc_"  + Id.getValue());
    if(!TransDirNew.exists())
        TransDirNew.createDirectory();
    TransientDir.setValue(TransDirNew.filePath());


    // SchemeVersion "2"
    if ( scheme == 2 ) {
        // read the feature types
        reader.readElement("Features");
        Cnt = reader.getAttributeAsInteger("Count");
        for (i=0 ;i<Cnt ;i++) {
            reader.readElement("Feature");
            string type = reader.getAttribute("type");
            string name = reader.getAttribute("name");

 //           try {
                addObject(type.c_str(),name.c_str());
//            }
//             catch ( Base::Exception& ) {
//                 Base::Console().Message("Cannot create object '%s'\n", name.c_str());
//             }
        }
        reader.readEndElement("Features");

        // read the features itself
        reader.readElement("FeatureData");
        Cnt = reader.getAttributeAsInteger("Count");
        for (i=0 ;i<Cnt ;i++) {
            reader.readElement("Feature");
            string name = reader.getAttribute("name");
            DocumentObject* pObj = getObject(name.c_str());
            if (pObj) { // check if this feature has been registered
                pObj->StatusBits.set(4);
                pObj->Restore(reader);
                pObj->StatusBits.reset(4);
            }
            reader.readEndElement("Feature");
        }
        reader.readEndElement("FeatureData");
    } // SchemeVersion "3" or higher
    else if ( scheme >= 3 ) {
        // read the feature types
        reader.readElement("Objects");
        Cnt = reader.getAttributeAsInteger("Count");
        for (i=0 ;i<Cnt ;i++) {
            reader.readElement("Object");
            string type = reader.getAttribute("type");
            string name = reader.getAttribute("name");

            
            addObject(type.c_str(),name.c_str());

        }
        reader.readEndElement("Objects");

        // read the features itself
        reader.readElement("ObjectData");
        Cnt = reader.getAttributeAsInteger("Count");
        for (i=0 ;i<Cnt ;i++) {
            reader.readElement("Object");
            string name = reader.getAttribute("name");
            DocumentObject* pObj = getObject(name.c_str());
            if (pObj) { // check if this feature has been registered
                pObj->StatusBits.set(4); 
                pObj->Restore(reader);
                pObj->StatusBits.reset(4);
            }
            reader.readEndElement("Object");
        }
        reader.readEndElement("ObjectData");
    }

    reader.readEndElement("Document");
}

void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
                             std::ostream& out)
{
//     Base::ZipWriter writer(out);
//     writer.putNextEntry("Document.xml");
//     writer.Stream() << "<?xml version='1.0' encoding='utf-8'?>" << endl;
//     writer.Stream() << "<Document SchemaVersion=\"4\">" << endl;
//     // Add this block to have the same layout as for normal documents
//     writer.Stream() << "<Properties Count=\"0\">" << endl;
//     writer.Stream() << "</Properties>" << endl;
// 
//     // writing the features types
//     writer.incInd(); // indention for 'Objects count'
//     writer.Stream() << writer.ind() << "<Objects Count=\"" << obj.size() <<"\">" << endl;
// 
//     writer.incInd(); // indention for 'Object type'
//     std::vector<DocumentObject*>::const_iterator it;
//     for (it = obj.begin(); it != obj.end(); ++it) {
//         writer.Stream() << writer.ind() << "<Object "
//         << "type=\"" << (*it)->getTypeId().getName() << "\" "
//         << "name=\"" << (*it)->getNameInDocument() << "\" "
//         << "/>" << endl;
//     }
// 
//     writer.decInd();  // indention for 'Object type'
//     writer.Stream() << writer.ind() << "</Objects>" << endl;
// 
//     // writing the features itself
//     writer.Stream() << writer.ind() << "<ObjectData Count=\"" << obj.size() <<"\">" << endl;
// 
//     writer.incInd(); // indention for 'Object name'
//     for (it = obj.begin(); it != obj.end(); ++it) {
//         writer.Stream() << writer.ind() << "<Object name=\"" << (*it)->getNameInDocument() << "\">" << endl;
//         (*it)->Save(writer);
//         writer.Stream() << writer.ind() << "</Object>" << endl;
//     }
// 
//     writer.decInd(); // indention for 'Object name'
//     writer.Stream() << writer.ind() << "</ObjectData>" << endl;
//     writer.decInd();  // indention for 'Objects count'
//     writer.Stream() << "</Document>" << endl;
// 
//     // Hook for others to add further data.
//     signalExportObjects(obj, writer);
// 
//     // write additional files
//     writer.writeFiles();
}

std::vector<App::DocumentObject*>
Document::importObjects(std::istream& input)
{
     std::vector<App::DocumentObject*> objs;
//     zipios::ZipInputStream zipstream(input);
//     Base::XMLReader reader("<memory>", zipstream);
// 
//     int i,Cnt;
//     reader.readElement("Document");
//     long scheme = reader.getAttributeAsInteger("SchemaVersion");
//     reader.DocumentSchema = scheme;
// 
//     // read the object types
//     std::map<std::string, std::string> nameMap;
//     reader.readElement("Objects");
//     Cnt = reader.getAttributeAsInteger("Count");
//     for (i=0 ;i<Cnt ;i++) {
//         reader.readElement("Object");
//         string type = reader.getAttribute("type");
//         string name = reader.getAttribute("name");
// 
//         try {
//             App::DocumentObject* o = addObject(type.c_str(),name.c_str());
//             objs.push_back(o);
//             // use this name for the later access because an object with
//             // the given name may already exist
//             nameMap[name] = o->getNameInDocument();
//         }
//         catch (Base::Exception&) {
//             Base::Console().Message("Cannot create object '%s'\n", name.c_str());
//         }
//     }
//     reader.readEndElement("Objects");
// 
//     // read the features itself
//     reader.readElement("ObjectData");
//     Cnt = reader.getAttributeAsInteger("Count");
//     for (i=0 ;i<Cnt ;i++) {
//         reader.readElement("Object");
//         std::string name = nameMap[reader.getAttribute("name")];
//         DocumentObject* pObj = getObject(name.c_str());
//         if (pObj) { // check if this feature has been registered
//             pObj->StatusBits.set(4);
//             pObj->Restore(reader);
//             pObj->StatusBits.reset(4);
//         }
//         reader.readEndElement("Object");
//     }
//     reader.readEndElement("ObjectData");
// 
//     reader.readEndElement("Document");
//     signalImportObjects(objs, reader);
//     reader.readFiles(zipstream);
//     // reset all touched
//     for (std::vector<DocumentObject*>::iterator it= objs.begin();it!=objs.end();++it)
//         (*it)->purgeTouched();
     return objs;


}

unsigned int Document::getMemSize (void) const
{
    unsigned int size = 0;

    // size of the DocObjects in the document
    std::vector<DocumentObject*>::const_iterator it;
    for (it = d->objectArray.begin(); it != d->objectArray.end(); ++it)
        size += (*it)->getMemSize();

    // size of the document properties...
    size += PropertyContainer::getMemSize();

    // Undo Redo size
    size += getUndoMemSize();

    return size;
}

// Save the document under the name it has been opened

// Save the document under the name it has been opened
bool Document::save (void)
{
	int compression = GetApp_Application().GetParameterGroupByPath
		("User parameter:BaseApp/Preferences/Document")->GetInt("CompressionLevel",3);

        std::string FilePath = FileName.getValue();

	if (*(FileName.getValue()) != '\0') {
		LastModifiedDate.setValue(Base::TimeInfo::currentDateTimeString());
		// make a tmp. file where to save the project data first and then rename to
		// the actual file name. This may be useful if overwriting an existing file
		// fails so that the data of the work up to now isn't lost.
		std::string uuid = Base::Uuid::CreateUuid();
		std::string fn = FileName.getValue();
		fn += "."; fn += uuid;
		Base::FileInfo tmp(fn);

		// open extra scope to close ZipWriter properly
		{
			Base::ofstream file(tmp, std::ios::out | std::ios::binary);
			Base::ZipWriter writer(file);

			writer.setComment("FreeCAD Document");
			writer.setLevel(compression);
			writer.putNextEntry("Document.xml");

			Document::Save(writer);

			// Special handling for Gui document.
			signalSaveDocument(writer);

			// write additional files
			writer.writeFiles();

			GetApp_Application().signalSaveDocument(*this);
		}

		// if saving the project data succeeded rename to the actual file name
		Base::FileInfo fi(FileName.getValue());
		if (fi.exists()) {
			bool backup = GetApp_Application().GetParameterGroupByPath
				("User parameter:BaseApp/Preferences/Document")->GetBool("CreateBackupFiles",true);
			int count_bak = GetApp_Application().GetParameterGroupByPath
				("User parameter:BaseApp/Preferences/Document")->GetInt("CountBackupFiles",1);
			if (backup) {
				int nSuff = 0;
				std::string fn = fi.fileName();
				Base::FileInfo di(fi.dirPath());
				std::vector<Base::FileInfo> backup;
				std::vector<Base::FileInfo> files = di.getDirectoryContent();
				for (std::vector<Base::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) {
					std::string file = it->fileName();
					if (file.substr(0,fn.length()) == fn) {
						// starts with the same file name
						std::string suf(file.substr(fn.length()));
						if (suf.size() > 0) {
							std::string::size_type nPos = suf.find_first_not_of("0123456789");
							if (nPos==std::string::npos) {
								// store all backup files
								backup.push_back(*it);
								nSuff = std::max<int>(nSuff, std::atol(suf.c_str()));
							}
						}
					}
				}

				if (!backup.empty() && (int)backup.size() >= count_bak) {
					// delete the oldest backup file we found
					Base::FileInfo del = backup.front();
					for (std::vector<Base::FileInfo>::iterator it = backup.begin(); it != backup.end(); ++it) {
						if (it->lastModified() < del.lastModified())
							del = *it;
					}

					del.deleteFile();
					fn = del.filePath();
				}
				else {
					// create a new backup file
					std::stringstream str;
					str << fi.filePath() << (nSuff + 1);
					fn = str.str();
				}

				fi.renameFile(fn.c_str());
			}
			else {
				fi.deleteFile();
			}
		}
		if (tmp.renameFile(FileName.getValue()) == false)
			Base::Console().Warning("Cannot rename file from '%s' to '%s'\n",
			fn.c_str(), FileName.getValue());

		return true;
	}

	return false;
}



bool Document::savecmo (const char* pn)
{
   GetApp_Application().getActiveDocument()->save();
  
// 	CCustomPlayer::Instance->save(pn);
//      int compression = GetApp_Application().GetParameterGroupByPath
//          ("User parameter:BaseApp/Preferences/Document")->GetInt("CompressionLevel",3);
// 
// 
// 	  std::string fn = GetApp_Application().getActiveDocument()->FileName.getValue();

   

    return false;
}

void Document::importObject(const char* objName)
{
	
	CCustomPlayer::Instance().ImportObject(objName);
}
void Document::restore(Base::XMLReader &reader)
{

	int i,Cnt;
	reader.readElement("Document");
	long scheme = reader.getAttributeAsInteger("SchemaVersion");
	reader.DocumentSchema = scheme;

	// When this document was created the FileName and Label properties
	// were set to the absolute path or file name, respectively. To save
	// the document to the file it was loaded from or to show the file name
	// in the tree view we must restore them after loading the file because
	// they will be overridden.
	// Note: This does not affect the internal name of the document in any way
	// that is kept in Application.
	std::string FilePath = FileName.getValue();
	std::string DocLabel = Label.getValue();

	// remove previous Transient directory
	Base::FileInfo TransDir(TransientDir.getValue());
	TransDir.deleteDirectoryRecursive();


	// read the Document Properties
	PropertyContainer::Restore(reader);

	// We must restore the correct 'FileName' property again because the stored
	// value could be invalid.
	FileName.setValue(FilePath.c_str());
	Label.setValue(DocLabel.c_str());

	// create new transient directory
	std::string basePath = Base::FileInfo::getTempPath() + GetApp_Application().getExecutableName();
	Base::FileInfo TransDirNew(basePath + "_Doc_"  + Id.getValue());
	if(!TransDirNew.exists())
		TransDirNew.createDirectory();
	TransientDir.setValue(TransDirNew.filePath());


	// SchemeVersion "2"
	if ( scheme == 2 ) {
		// read the feature types
		reader.readElement("Features");
		Cnt = reader.getAttributeAsInteger("Count");
		for (i=0 ;i<Cnt ;i++) {
			reader.readElement("Feature");
			string type = reader.getAttribute("type");
			string name = reader.getAttribute("name");

		//	try {
				addObject(type.c_str(),name.c_str());
// 			}
// 			catch ( Base::Exception& ) {
// 				Base::Console().Message("Cannot create object '%s'\n", name.c_str());
// 			}
		}
		reader.readEndElement("Features");

		// read the features itself
		reader.readElement("FeatureData");
		Cnt = reader.getAttributeAsInteger("Count");
		for (i=0 ;i<Cnt ;i++) {
			reader.readElement("Feature");
			string name = reader.getAttribute("name");
			DocumentObject* pObj = getObject(name.c_str());
			if (pObj) { // check if this feature has been registered
				pObj->StatusBits.set(4);
				pObj->Restore(reader);
				pObj->StatusBits.reset(4);
			}
			reader.readEndElement("Feature");
		}
		reader.readEndElement("FeatureData");
	} // SchemeVersion "3" or higher
	else if ( scheme >= 3 ) {
		// read the feature types
		reader.readElement("Objects");
		Cnt = reader.getAttributeAsInteger("Count");
		for (i=0 ;i<Cnt ;i++) {
			reader.readElement("Object");
			string type = reader.getAttribute("type");
			string name = reader.getAttribute("name");

//			try {
				addObject(type.c_str(),name.c_str());
// 			}
// 			catch ( Base::Exception& ) {
// 				Base::Console().Message("Cannot create object '%s'\n", name.c_str());
// 			}
		}
		reader.readEndElement("Objects");

		// read the features itself
		reader.readElement("ObjectData");
		Cnt = reader.getAttributeAsInteger("Count");
		for (i=0 ;i<Cnt ;i++) {
			reader.readElement("Object");
			string name = reader.getAttribute("name");
			DocumentObject* pObj = getObject(name.c_str());
			if (pObj) { // check if this feature has been registered
				pObj->StatusBits.set(4);
				pObj->Restore(reader);
				pObj->StatusBits.reset(4);
			}
			reader.readEndElement("Object");
		}
		reader.readEndElement("ObjectData");
	}

	reader.readEndElement("Document");

// 	GetApp_Application().getActiveDocument()->restore();
//   //  if(CCustomPlayer::Instance->clearScene())
// 	//{
// 		CCustomPlayer::Instance->_Load(pathname);
// 		//GetApp_Application().signalRestoreDocument(*this);
// 	//}

}
// Open the document
void Document::restore (void)
{
	// clean up if the document is not empty
	// !TODO mind exeptions while restoring!
	clearUndos();
	for (std::vector<DocumentObject*>::iterator obj = d->objectArray.begin(); obj != d->objectArray.end(); ++obj) {
		signalDeletedObject(*(*obj));
		delete *obj;
	}
	d->objectArray.clear();
	d->objectMap.clear();
	d->activeObject = 0;

	Base::FileInfo fi(FileName.getValue());
	Base::ifstream file(fi, std::ios::in | std::ios::binary);
	std::streambuf* buf = file.rdbuf();
	std::streamoff size = buf->pubseekoff(0, std::ios::end, std::ios::in);
	buf->pubseekoff(0, std::ios::beg, std::ios::in);
	if (size < 22) // an empty zip archive has 22 bytes
	return; //	throw Base::FileException("Invalid project file",FileName.getValue());

	zipios::ZipInputStream zipstream(file);
	Base::XMLReader reader(FileName.getValue(), zipstream);

	if (!reader.isValid())
		return; //throw Base::FileException("Error reading compression file",FileName.getValue());


	Document::Restore(reader);

	signalRestoreDocument(reader);
	reader.readFiles(zipstream);

	// reset all touched
	for (std::map<std::string,DocumentObject*>::iterator It= d->objectMap.begin();It!=d->objectMap.end();++It)
		It->second->purgeTouched();

	GetApp_Application().signalRestoreDocument(*this);



}

bool Document::isSaved() const
{
    std::string name = FileName.getValue();
    return !name.empty();
}

/** Label is the visible name of a document shown e.g. in the windows title
 * or in the tree view. The label almost (but not always e.g. if you manually change it)
 * matches with the file name where the document is stored to.
 * In contrast to Label the method getName() returns the internal name of the document that only
 * matches with Label when loading or creating a document because then both are set to the same value.
 * Since the internal name cannot be changed during runtime it must differ from the Label after saving 
 * the document the first time or saving it under a new file name.
 * @ note More than one document can have the same label name.
 * @ note The internal is always guaranteed to be unique because @ref Application::newDocument() checks
 * for a document with the same name and makes it unique if needed. Hence you cannot rely on that the
 * internal name matches with the name you passed to Application::newDoument(). You should use the
 * method getName() instead.
 */
const char* Document::getName() const
{
    return GetApp_Application().getDocumentName(this);

	//return "app_Document_name";

}

/// Remove all modifications. After this call The document becomes valid again.
void Document::purgeTouched()
{
    for (std::vector<DocumentObject*>::iterator It = d->objectArray.begin();It != d->objectArray.end();++It)
        (*It)->purgeTouched();
}

bool Document::isTouched() const
{
    for (std::vector<DocumentObject*>::const_iterator It = d->objectArray.begin();It != d->objectArray.end();++It)
        if ((*It)->isTouched())
            return true;
    return false;
}

vector<DocumentObject*> Document::getTouched(void) const
{
    vector<DocumentObject*> result;

    for (std::vector<DocumentObject*>::const_iterator It = d->objectArray.begin();It != d->objectArray.end();++It)
        if ((*It)->isTouched())
            result.push_back(*It);

    return result;
}

void Document::setClosable(bool c)
{
    d->closable = c;
}

bool Document::isClosable() const
{
    return d->closable;
}

int Document::countObjects(void) const
{
   return static_cast<int>(d->objectArray.size());
}

std::vector<App::DocumentObject*> Document::getInList(const DocumentObject* me) const
{
    // result list
    std::vector<App::DocumentObject*> result;
    // go through all objects
    for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
        // get the outList and search if me is in that list
        std::vector<DocumentObject*> OutList = It->second->getOutList();
        for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
            if (*It2 && *It2 == me)
                // add the parent object
                result.push_back(It->second);
    }
    return result;
}
bool Document::recomputeEntity(const char* val01,const char* val02)
{
	GetApp_Application().getActiveDocument()->recompute();
	return true;
}
void Document::recompute()
{
    // delete recompute log
    for( std::vector<App::DocumentObjectExecReturn*>::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
        delete *it;
    _RecomputeLog.clear();

    DependencyList DepList;
    std::map<DocumentObject*,Vertex> VertexObjectList;

    // Filling up the adjacency List
    for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It)
        // add the object as Vertex and remember the index
        VertexObjectList[It->second] = add_vertex(DepList);
    // add the edges
    for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
        std::vector<DocumentObject*> OutList = It->second->getOutList();
        for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
            if (*It2)
                add_edge(VertexObjectList[It->second],VertexObjectList[*It2],DepList);
    }

    std::list<Vertex> make_order;
    DependencyList::out_edge_iterator j, jend;
    // this sort gives the execute
    boost::topological_sort(DepList, std::front_inserter(make_order));

    // caching vertex to DocObject
    for (std::map<DocumentObject*,Vertex>::const_iterator It1= VertexObjectList.begin();It1 != VertexObjectList.end(); ++It1)
        d->vertexMap[It1->second] = It1->first;

#ifdef FC_LOGFEATUREUPDATE
    std::clog << "make ordering: " << std::endl;
#endif

    for (std::list<Vertex>::reverse_iterator i = make_order.rbegin();i != make_order.rend(); ++i) {
        DocumentObject* Cur = d->vertexMap[*i];
        if (!Cur) continue;
#ifdef FC_LOGFEATUREUPDATE
        std::clog << Cur->getNameInDocument() << " dep on: " ;
#endif
        bool NeedUpdate = false;

        // ask the object if it should be recomputed
        if (Cur->mustExecute() == 1)
            NeedUpdate = true;
        else {// if (Cur->mustExecute() == -1)
            // update if one of the dependencies is touched
            for (boost::tie(j, jend) = out_edges(*i, DepList); j != jend; ++j) {
                DocumentObject* Test = d->vertexMap[target(*j, DepList)];
                if (!Test) continue;
#ifdef FC_LOGFEATUREUPDATE
                std::clog << Test->getNameInDocument() << ", " ;
#endif
                if (Test->isTouched()) {
                    NeedUpdate = true;
                    break;
                }
            }
#ifdef FC_LOGFEATUREUPDATE
            std::clog << std::endl;
#endif
        }
        // if one touched recompute
        if (NeedUpdate) {
#ifdef FC_LOGFEATUREUPDATE
            std::clog << "Recompute" << std::endl;
#endif
            if (_recomputeFeature(Cur)) {
                // if somthing happen break execution of recompute
                d->vertexMap.clear();
                return;
            }
        }
    }

    // reset all touched
    for (std::map<Vertex,DocumentObject*>::iterator it = d->vertexMap.begin(); it != d->vertexMap.end(); ++it) {
        if (it->second)
            it->second->purgeTouched();
    }
    d->vertexMap.clear();
}

const char * Document::getErrorDescription(const App::DocumentObject*Obj) const
{
    for (std::vector<App::DocumentObjectExecReturn*>::const_iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
        if ((*it)->Which == Obj)
            return (*it)->Why.c_str();
    return 0;
}

// call the recompute of the Feature and handle the exceptions and errors.
bool Document::_recomputeFeature(DocumentObject* Feat)
{
// 
// #ifdef FC_LOGFEATUREUPDATE
//     std::clog << "Solv: Executing Feature: " << Feat->getNameInDocument() << std::endl;;
// #endif
// 
     DocumentObjectExecReturn  *returnCode = 0;
//     try {
         returnCode = Feat->recompute();
//     }
//     catch(Base::AbortException &e){
//         e.ReportException();
//         _RecomputeLog.push_back(new DocumentObjectExecReturn("User abort",Feat));
//         Feat->setError();
//         return true;
//     }
//     catch (const Base::MemoryException& e) {
//         Base::Console().Error("Memory exception in feature '%s' thrown: %s\n",Feat->getNameInDocument(),e.what());
//         _RecomputeLog.push_back(new DocumentObjectExecReturn("Out of memory exception",Feat));
//         Feat->setError();
//         return true;
//     }
//     catch (Base::Exception &e) {
//         e.ReportException();
//         _RecomputeLog.push_back(new DocumentObjectExecReturn(e.what(),Feat));
//         Feat->setError();
//         return false;
//     }
//     catch (std::exception &e) {
//         Base::Console().Warning("exception in Feature \"%s\" thrown: %s\n",Feat->getNameInDocument(),e.what());
//         _RecomputeLog.push_back(new DocumentObjectExecReturn(e.what(),Feat));
//         Feat->setError();
//         return false;
//     }
// #ifndef FC_DEBUG
//     catch (...) {
//         Base::Console().Error("App::Document::_RecomputeFeature(): Unknown exception in Feature \"%s\" thrown\n",Feat->getNameInDocument());
//         _RecomputeLog.push_back(new DocumentObjectExecReturn("Unknown exeption!"));
//         Feat->setError();
//         return true;
//     }
// #endif
// 
//     // error code
//     if (returnCode == DocumentObject::StdReturn) {
//         Feat->resetError();
//     }
//     else {
//         returnCode->Which = Feat;
//         _RecomputeLog.push_back(returnCode);
//         Base::Console().Error("%s\n",returnCode->Why.c_str());
//         Feat->setError();
//     }
    return false;
}

void Document::recomputeFeature(DocumentObject* Feat)
{
     // delete recompute log
    for( std::vector<App::DocumentObjectExecReturn*>::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
        delete *it;
    _RecomputeLog.clear();

    _recomputeFeature(Feat);
}
void Document::addvrmlObjCustomProperty(const char* sType,CKCharacter* cha)
{
// 	Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
// 	if(base)
// 	{
// 		assert(base->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId()));
// 		ViewProviderVRMLObject *pcProvider = static_cast<ViewProviderVRMLObject*>(base);
// 		pcProvider->addCustomProperty(cha);
// 	}
}
DocumentObject* Document::addObject(const char* sType, const char* pObjectName/*,CKCharacter* cha*/)
{
     Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
// 
     string	 ObjectName	;
     if (!base)
         return 0;
     if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
         delete base;
//          std::stringstream str;
//          str << "'" << sType << "' is not a document object type";
//          throw Base::Exception(str.str());
     }

    
     App::DocumentObject* pcObject = static_cast<App::DocumentObject*>(base);
     pcObject->setDocument(this);
//      if(cha != NULL)
// 		 pcObject->setEntity(cha);

	// pcObject->setEntity(CCustomPlayer::Instance->getObjectByName(pObjectName));
// 
//     // Transaction stuff
//      if (d->activeTransaction)
//          d->activeTransaction->addObjectNew(pcObject);
//      // Undo stuff
//      if (d->activeUndoTransaction)
//          d->activeUndoTransaction->addObjectDel(pcObject);
 
//     // get Unique name
     if (pObjectName)
         ObjectName = getUniqueObjectName(pObjectName);
     else
         ObjectName = getUniqueObjectName(sType);
 
// 
     d->activeObject = pcObject;
// 
//     // insert in the name map
    d->objectMap[ObjectName] = pcObject;
     // cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
     pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
     // insert in the vector
     d->objectArray.push_back(pcObject);
     // insert in the adjacence list and referenc through the ConectionMap
     //_DepConMap[pcObject] = add_vertex(_DepList);
 
     pcObject->Label.setValue( ObjectName );
 
     // mark the object as new (i.e. set status bit 2) and send the signal
     pcObject->StatusBits.set(2);
     signalNewObject(*pcObject);
     signalActivatedObject(*pcObject);
 
     // return the Object
    return pcObject;

	
}

void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
{
//     d->objectMap[pObjectName] = pcObject;
//     d->objectArray.push_back(pcObject);
//     // cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
//     pcObject->pcNameInDocument = &(d->objectMap.find(pObjectName)->first);
// 
//     // Transaction stuff
//     if (d->activeTransaction)
//         d->activeTransaction->addObjectNew(pcObject);
//     // Undo stuff
//     if (d->activeUndoTransaction)
//         d->activeUndoTransaction->addObjectDel(pcObject);
// 
//     // send the signal
//     signalNewObject(*pcObject);
}
void Document::resetDocument_S(const char * Name, const char * UserName)
{
   GetApp_Application().getActiveDocument()->resetDocument();
}
bool Document::resetDocument()
{
	while(!d->objectArray.empty())
	{
		std::vector<DocumentObject*>::iterator obj = d->objectArray.begin();
       remObject((*obj)->getNameInDocument());
	}
// 	while(!d->objectArray.empty())
// 	{
//         
// 	}
// 	for (std::vector<DocumentObject*>::iterator obj = d->objectArray.begin(); obj != d->objectArray.end(); ++obj) {
// 
// 		signalDeletedObject(*(*obj));
// 		delete *obj;
// 	}
	d->objectArray.clear();
	d->objectMap.clear();
	d->activeObject = 0;
  return true;

}
void Document::remvoeObject(const char* oName,const char* indocumentName)
{
	GetApp_Application().getActiveDocument()->remObject(indocumentName);
}

/// Remove an object out of the document
void Document::remObject(const char* sName)
{

     std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(sName);


 
     // name not found?
     if (pos == d->objectMap.end())
         return;
 
      signalDeletedObject(*(pos->second));
	  delete(pos->second);

	 if (d->activeObject == pos->second)
         d->activeObject = 0;
 
   
//      if (!d->vertexMap.empty()) {
//          // recompute of document is running
//          for (std::map<Vertex,DocumentObject*>::iterator it = d->vertexMap.begin(); it != d->vertexMap.end(); ++it) {
//              if (it->second == pos->second) {
//                  it->second = 0; // just nullify the pointer
//                  break;
//              }
//          }
//      }
 
     // Before deleting we must nullify all dependant objects
     //breakDependency(pos->second, true);
 
     // Transaction stuff
//      if (d->activeTransaction)
//          d->activeTransaction->addObjectDel(pos->second);
 
     // Undo stuff
//      if (d->activeUndoTransaction) {
//          // in this case transaction delete or save the object
//          d->activeUndoTransaction->addObjectNew(pos->second);
//          // set name cache false
//          //pos->second->pcNameInDocument = 0;
//      }
//      else
//          // if not saved in undo -> delete object
//          delete pos->second;
 
 
     for (std::vector<DocumentObject*>::iterator obj = d->objectArray.begin(); obj != d->objectArray.end(); ++obj) {
         if (*obj == pos->second) {
             d->objectArray.erase(obj);
             break;
         }
     }
     // remove from adjancy list
     //remove_vertex(_DepConMap[pos->second],_DepList);
     //_DepConMap.erase(pos->second);
     d->objectMap.erase(pos);
}

/// Remove an object out of the document (internal)
void Document::_remObject(DocumentObject* pcObject)
{
    std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(pcObject->getNameInDocument());

    if (d->activeObject == pcObject)
        d->activeObject = 0;

    signalDeletedObject(*pcObject);

//     // Transaction stuff
//     if (d->activeTransaction)
//         d->activeTransaction->addObjectDel(pcObject);
// 
//     // Undo stuff
//     if (d->activeUndoTransaction)
//         d->activeUndoTransaction->addObjectNew(pcObject);

    // remove from map
    d->objectMap.erase(pos);
    //// set name cache false
    //pcObject->pcNameInDocument = 0;

    for (std::vector<DocumentObject*>::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
        if (*it == pcObject) {
            d->objectArray.erase(it);
            break;
        }
    }
}

void Document::breakDependency(DocumentObject* pcObject, bool clear)
{
    // Nullify all dependant objects
    for (std::map<std::string,DocumentObject*>::iterator it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
        std::map<std::string,App::Property*> Map;
        it->second->getPropertyMap(Map);
        // search for all properties that could have a link to the object
        for (std::map<std::string,App::Property*>::iterator pt = Map.begin(); pt != Map.end(); ++pt) {
            if (pt->second->getTypeId().isDerivedFrom(PropertyLink::getClassTypeId())) {
                PropertyLink* link = static_cast<PropertyLink*>(pt->second);
                if (link->getValue() == pcObject)
                    link->setValue(0);
                else if (link->getContainer() == pcObject && clear)
                    link->setValue(0);
            }
            else if (pt->second->getTypeId().isDerivedFrom(PropertyLinkList::getClassTypeId())) {
                PropertyLinkList* link = static_cast<PropertyLinkList*>(pt->second);
                if (link->getContainer() == pcObject && clear) {
                    link->setValues(std::vector<DocumentObject*>());
                }
                else {
                    // copy the list (not the objects)
                    std::vector<DocumentObject*> linked = link->getValues();
                    for (std::vector<DocumentObject*>::iterator fIt = linked.begin(); fIt != linked.end(); ++fIt) {
                        if ((*fIt) == pcObject) {
                            // reassign the the list without the object to be deleted
                            linked.erase(fIt);
                            link->setValues(linked);
                            break;
                        }
                    }
                }
            }
        }
    }
}

DocumentObject* Document::_copyObject(DocumentObject* obj, std::map<DocumentObject*, 
                                      DocumentObject*>& copy_map, bool recursive)
{
    if (!obj) return 0;
    DocumentObject* copy = addObject(obj->getTypeId().getName(),obj->getNameInDocument());
    if (!copy) return 0;

    copy_map[obj] = copy;

    std::map<std::string,App::Property*> props;
    copy->getPropertyMap(props);
    for (std::map<std::string,App::Property*>::iterator it = props.begin(); it != props.end(); ++it) {
        App::Property* prop = obj->getPropertyByName(it->first.c_str());
        if (prop && prop->getTypeId() == it->second->getTypeId()) {
            if (prop->getTypeId() == PropertyLink::getClassTypeId()) {
                DocumentObject* link = static_cast<PropertyLink*>(prop)->getValue();
                std::map<DocumentObject*, DocumentObject*>::iterator pt = copy_map.find(link);
                if (pt != copy_map.end()) {
                    // the object has already been copied
                    static_cast<PropertyLink*>(it->second)->setValue(pt->second);
                }
                else if (recursive) {
                    DocumentObject* link_copy = _copyObject(link, copy_map, recursive);
                    copy_map[link] = link_copy;
                    static_cast<PropertyLink*>(it->second)->setValue(link_copy);
                }
                else if (link && link->getDocument() == this) {
                    //static_cast<PropertyLink*>(it->second)->setValue(link);
                }
            }
            else if (prop->getTypeId() == PropertyLinkList::getClassTypeId()) {
                std::vector<DocumentObject*> links = static_cast<PropertyLinkList*>(prop)->getValues();
                if (recursive) {
                    std::vector<DocumentObject*> links_copy;
                    for (std::vector<DocumentObject*>::iterator jt = links.begin(); jt != links.end(); ++jt) {
                        std::map<DocumentObject*, DocumentObject*>::iterator pt = copy_map.find(*jt);
                        if (pt != copy_map.end()) {
                            // the object has already been copied
                            links_copy.push_back(pt->second);
                        }
                        else {
                            links_copy.push_back(_copyObject(*jt, copy_map, recursive));
                            copy_map[*jt] = links_copy.back();
                        }
                    }
                    static_cast<PropertyLinkList*>(it->second)->setValues(links_copy);
                }
                else {
                    std::vector<DocumentObject*> links_ref;
                    //for (std::vector<DocumentObject*>::iterator jt = links.begin(); jt != links.end(); ++jt) {
                    //    if ((*jt)->getDocument() == this)
                    //        links_ref.push_back(*jt);
                    //}
                    static_cast<PropertyLinkList*>(it->second)->setValues(links_ref);
                }
            }
            else {
                std::auto_ptr<Property> data(prop->Copy());
                if (data.get()) {
                    it->second->Paste(*data);
                }
            }
        }
    }

     // unmark to be not re-computed later
    copy->purgeTouched();
    return copy;
}

DocumentObject* Document::copyObject(DocumentObject* obj, bool recursive)
{
    std::map<DocumentObject*, DocumentObject*> copy_map;
    DocumentObject* copy = _copyObject(obj, copy_map, recursive);
    return copy;
}

DocumentObject* Document::moveObject(DocumentObject* obj, bool recursive)
{
    Document* that = obj->getDocument();
    if (that == this)
        return 0; // nothing todo

    // all object of the other document that refer to this object must be nullified
    that->breakDependency(obj, false);
    std::string objname = getUniqueObjectName(obj->getNameInDocument());
    that->_remObject(obj);
    this->_addObject(obj, objname.c_str());
    obj->setDocument(this);

    std::map<std::string,App::Property*> props;
    obj->getPropertyMap(props);
    for (std::map<std::string,App::Property*>::iterator it = props.begin(); it != props.end(); ++it) {
        if (it->second->getTypeId() == PropertyLink::getClassTypeId()) {
            DocumentObject* link = static_cast<PropertyLink*>(it->second)->getValue();
            if (recursive) {
                moveObject(link, recursive);
                static_cast<PropertyLink*>(it->second)->setValue(link);
            }
            else {
                static_cast<PropertyLink*>(it->second)->setValue(0);
            }
        }
        else if (it->second->getTypeId() == PropertyLinkList::getClassTypeId()) {
            std::vector<DocumentObject*> links = static_cast<PropertyLinkList*>(it->second)->getValues();
            if (recursive) {
                for (std::vector<DocumentObject*>::iterator jt = links.begin(); jt != links.end(); ++jt)
                    moveObject(*jt, recursive);
                static_cast<PropertyLinkList*>(it->second)->setValues(links);
            }
            else {
                static_cast<PropertyLinkList*>(it->second)->setValues(std::vector<DocumentObject*>());
            }
        }
    }

    return obj;
}

DocumentObject * Document::getActiveObject(void) const
{
    return d->activeObject;
}

DocumentObject * Document::getObject(const char *Name) const
{
    std::map<std::string,DocumentObject*>::const_iterator pos;

    pos = d->objectMap.find(Name);

    if (pos != d->objectMap.end())
        return pos->second;
    else
        return 0;
}

const char * Document::getObjectName(DocumentObject *pFeat) const
{
    std::map<std::string,DocumentObject*>::const_iterator pos;

    for (pos = d->objectMap.begin();pos != d->objectMap.end();++pos)
        if (pos->second == pFeat)
            return pos->first.c_str();

    return 0;
}

std::string Document::getUniqueObjectName(const char *Name) const
{
     if (!Name || *Name == '\0')
         return std::string();
     std::string CleanName = Base::Tools::getIdentifier(Name);
 
     // name in use?
     std::map<std::string,DocumentObject*>::const_iterator pos;
     pos = d->objectMap.find(CleanName);
 
     if (pos == d->objectMap.end()) {
         // if not, name is OK
         return CleanName;
     }
     else {
         std::vector<std::string> names;
         names.reserve(d->objectMap.size());
         for (pos = d->objectMap.begin();pos != d->objectMap.end();++pos) {
             names.push_back(pos->first);
         }
         return Base::Tools::getUniqueName(CleanName, names, 3);
     }
}

std::string Document::getStandardObjectName(const char *Name, int d) const
{
     std::vector<App::DocumentObject*> mm = getObjects();
     std::vector<std::string> labels;
     labels.reserve(mm.size());
 
     for (std::vector<App::DocumentObject*>::const_iterator it = mm.begin(); it != mm.end(); ++it) {
         std::string label = (*it)->Label.getValue();
         labels.push_back(label);
     }
     return Base::Tools::getUniqueName(Name, labels, d);

	//return "";
}

std::vector<DocumentObject*> Document::getObjects() const
{
    return d->objectArray;
}

std::vector<DocumentObject*> Document::getObjectsOfType(const Base::Type& typeId) const
{
    std::vector<DocumentObject*> Objects;
	int a = d->objectArray.size();
    for (std::vector<DocumentObject*>::const_iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
        if ((*it)->getTypeId().isDerivedFrom(typeId))
            Objects.push_back(*it);
    }
    return Objects;
}

int Document::countObjectsOfType(const Base::Type& typeId) const
{
    int ct=0;
    for (std::map<std::string,DocumentObject*>::const_iterator it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
        if (it->second->getTypeId().isDerivedFrom(typeId))
            ct++;
    }

    return ct;
}
// 
// PyObject * Document::getPyObject(void)
// {
//     return Py::new_reference_to(DocumentPythonObject);
// }
